home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 1.iso
/
toolbox
/
src
/
exampleCode
/
opengl
/
xlib
/
tabletogl.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-11-11
|
24KB
|
643 lines
/*
* Copyright (c) 1993-94, Silicon Graphics, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the name of Silicon Graphics may not be used in any advertising or
* publicity relating to the software without the specific, prior written
* permission of Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE
* POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* OpenGL(TM) is a trademark of Silicon Graphics, Inc.
*/
/*
* tabletogl.c : an openGL-Xlib tablet "line-drawing" demo program.
* This is the "after" version, ported from its 4.0
* GLX mixed model "before" counterpart, located at
* ../../GLX/tablet/tabletglx.c
*
* tabletogl imitates a pen (stylus) drawing on a surface:
* while the pen (stylus' button) is pressed down, a line
* continues to be drawn. when the pen is released, the
* current line stops.
*
* There are a function prior to the infinite loop worth noting:
*
* setupdevs() finds, opens, and creates a handle to the "tablet"
* device structure. it then determines the given type and class
* of each device event we're going to be interested in, and then
* makes requests to the server to send us events that match the
* events and devices described by the event list *and* that come
* from our specific window.
*
* Following this, the get/process input infinite loop occupies the
* rest of the program's energies. the core of this is the "default"
* portion of the "switch (event.type)" statement which catches the
* tablet events being generated. notice this is where the
* {tablet_motion_type, tablet_press_type, tablet_release_type} vars
* come into play: recall these were defined in setupdevs() with the
* 3 macros DeviceMotionNotify, DeviceButtonPress, and
* DeviceButtonRelease, respectively. XSelectExtensionEvent then was
* used to ask the server to send us any events generated by these
* devices in our window.
* ratmandu -- ported to openGL, aug 93
*/
#include <stdio.h>
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/extensions/XI.h>
#include <X11/extensions/XInput.h>
#define YELLOW 3
#define BLUE 4
#define CYAN 6
#define X 0
#define Y 1
#define MAXRESOLUTION 2206
/* line drawing structures to create a new linked-list everytime the
* stylus (button) is pressed down and grow that list as long as
* MotionNotify events occur. when the stylus (button) is released,
* free up the list and reset the head and tail to point to null
*/
#define newll() ((struct lineElement *)malloc(sizeof(struct lineElement)))
struct lineElement {
long xy[2];
struct lineElement *next;
};
typedef struct {
struct lineElement *head, *tail;
} LineList;
LineList lineLs;
struct lineElement *lineDummy;
Display *dpy; /* The X server connection */
Atom del_atom; /* WM_DELETE_WINDOW atom */
Window glwin; /* handle to the GL window */
static void openwindow(char *);
static void setupdevs(void);
static void makeframe(void);
static void drawcurrentline(void);
static void clean_exit(void);
static void makeRasterFont(Display **dpy);
static void printString(char *s);
int tablet_device_id; /* device handles for the */
int tablet_press_type; /* the following 3 tablet device ID handles are */
int tablet_release_type; /* defined in setupdevs() and then used in the */
int tablet_motion_type; /* infinite (get/process input) loop in main */
float ratio; /* stores the current ratio to scale the tablet's full */
/* range of coordinates into the window's current size */
int xsize, ysize; /* stores window's current size values */
XEvent event;
void main(int argc, char *argv[])
{
int i;
long xprev, yprev; /* used if either the x or y stylus val doesn't chng */
int myExpose, myConfigure, myMotion,
myButtPress, myButtRelease, myButtDown; /* store which events occur */
myExpose = myConfigure = myMotion = GL_FALSE;
myButtPress = myButtRelease = myButtDown = GL_FALSE;
openwindow(argv[0]); /* open "window" something like winopen wud do */
setupdevs(); /* make the necessary connections to the tablet */
makeRasterFont(&dpy); /* make font */
/*
* The event loop.
*/
while (1) { /* standard logic: get event(s), process event(s) */
int axis_data[6];
glFlush(); /* For proper DGL performance */
/* this "do while" loop does the `get events' half of the "get events,
* process events" action of the infinite while. this is to ensure
* the event queue is always drained before the events that have come
* in are processed.
*/
do {
XNextEvent(dpy, &event);
switch (event.type) {
/* "Expose" events are sort of like "REDRAW" in gl-speak in
* terms of when a window becomes visible, or a previously
* invisible part becomes visible.
*/
case Expose: /* Exposures */
myExpose = GL_TRUE;
break;
/* "ConfigNotify" events are like "REDRAW" in terms of changes
* to a window's size or position.
*/
case ConfigureNotify: /* Resize GL manually */
/* save the changed width/height of the parent X window */
xsize = event.xconfigure.width;
ysize = event.xconfigure.height;
ratio = (float) xsize / MAXRESOLUTION;
myConfigure = GL_TRUE;
break;
case ButtonRelease: /* Back door exit */
if (event.xbutton.button == Button1)
clean_exit();
break;
case ClientMessage: /* WM invoked exit */
if (event.xclient.data.l[0] == del_atom)
clean_exit();
break;
/* since interest is on the tablet, it becomes the default */
default:
if ((lineLs.tail != NULL) && /* make sure we've already
processed a button (stylus)
press event which sets up
the linked-list for x/y
pair storage/line drawing */
(event.type == tablet_motion_type) && /* make sure
this is a mo-
tion event */
myButtDown) { /* and make sure button itself is still
down--myButtPress only processes the
occurence of the but press (which
sets up the new linked list) and
then is immediately reset to FALSE */
/* the body of this if statement processes "Motion" events
* from any one of the dials. the axes_count element of
* the XDeviceMotionEvent structure (defined in /usr/include
* /X11/extensions/XInput.h) is used to determine if there
* are 2 or only 1 new coordinate value(s):
* if (axes_count == 2), both X and Y have changed,
* if (axes_count == 1), only Y has changed--X has not, and
* if (axes_count == 0), only X has changed--Y has not.
*/
XDeviceMotionEvent *M = (XDeviceMotionEvent *) &event;
if (M->axes_count != 2) { /* if x OR y didn't chng */
xprev = lineLs.tail->xy[X];/* axes_count < 2 so */
yprev = lineLs.tail->xy[Y];/* need to save prev */
}
lineDummy = newll(); /* alloc a new element */
lineDummy->next = NULL; /* for current line */
lineLs.tail->next = lineDummy; /* point tail to it */
lineLs.tail = lineDummy;
if (M->axes_count == 2) { /* if new x/y pair, */
lineLs.tail->xy[X] = M->axis_data[X];
lineLs.tail->xy[Y] = M->axis_data[Y];
} else if (M->first_axis == 0) {/* elseif new X val */
lineLs.tail->xy[X] = M->axis_data[0];
lineLs.tail->xy[Y] = yprev;
} else if (M->first_axis == 1) {/* elseif new Y val */
lineLs.tail->xy[X] = xprev;
lineLs.tail->xy[Y] = M->axis_data[0];
}
myMotion = GL_TRUE;
} else if (event.type == tablet_press_type) {
XDeviceButtonEvent *P = (XDeviceButtonEvent *) &event;
xprev = P->axis_data[0]; /* butt's been pressed so */
yprev = P->axis_data[1]; /* now start to make a new */
myButtPress = GL_TRUE; /* line. this cur pnt'll */
myButtDown = GL_TRUE; /* be the "move to" coord */
} else if (event.type == tablet_release_type) {
myButtRelease = GL_TRUE;
myButtDown = GL_FALSE;
}
break;
} /* end switch (event.type) */
} while (XPending(dpy)); /* end "do { } while".
* XPending() is like qtest()--it only
* tells you if there're any events
* presently in the queue. it does not
* disturb queue's contents in any way.
*/
/* On an "Expose" event, redraw the affected window
*/
if (myExpose) {
makeframe(); /* draw the GL stuff */
myExpose = GL_FALSE; /* reset flag--queue now empty */
}
/* On a "ConfigureNotify" event, resize window (XMoveResizeWindow),
* and then redraw contents.
*/
if (myConfigure) {
glViewport(0, 0, xsize-1, ysize-1);
makeframe();
myConfigure = GL_FALSE; /* reset flag--queue now empty */
}
/* a motion-type event (the next x/y pair was already saved up above)
* means we're still drawing more along the current line.
*/
if (myMotion) {
drawcurrentline(); /* butt still pressed, pen still moving, */
/* keep drawing at end of current line */
myMotion = GL_FALSE; /* reset flag--queue now empty */
}
/* a "button press"-type event means we're starting a new line so we
* need to re-initialize/create our linked-list.
*/
if (myButtPress) {
XDeviceButtonEvent *B = (XDeviceButtonEvent *) &event;
lineDummy = newll(); /* making a new line so start a */
lineDummy->next = NULL; /* new list. point head and tail */
lineLs.head = lineDummy; /* to it, and assign current new */
lineLs.tail = lineDummy; /* point to "tail" of list */
lineLs.tail->xy[X] = xprev; /* make our first point be what */
lineLs.tail->xy[Y] = yprev; /* we saved up above */
myButtPress = GL_FALSE; /* reset flag--queue now empty */
}
/* a "button release"-type event means the current line is complete
* so now we need to free up the current linked-list.
*/
if (myButtRelease) {
struct lineElement *ptr;
for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
free(ptr); /* "empty" our current list */
}
lineLs.head = NULL;
lineLs.tail = NULL;
myButtRelease = GL_FALSE; /* reset flag--queue now empty */
}
}
}
static int attributeList[] = { None }; /* use the default graphics visual */
/* WaitForNotify:
* used to make sure the MapWindow() calls inside openwindow() occur
* beFORE glXMakeCurrent() is invoked so as to avoid a race condition.
*/
static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
}
/* openwindow -
* establish connection to X server, get screen info, specify the
* attributes we want the WM to try to provide, and create the GL window
*/
static void openwindow(char *progname) {
int scrnnum; /* X screen number */
int xorig, yorig; /* window (upper-left) origin */
long scrnheight;
XSizeHints Winhints; /* used to fix window size */
Colormap cmap;
GLXContext cx;
XVisualInfo *vi;
XSetWindowAttributes swa;
XColor colorstruct;
/* Connect to the X server and get screen info */
if ((dpy = XOpenDisplay(NULL)) == NULL) {
fprintf(stderr, "%s: cannot connect to X server %s\n",
progname, XDisplayName(NULL));
exit(1);
}
scrnnum = DefaultScreen(dpy);
scrnheight = DisplayHeight(dpy, scrnnum);
/* define window (upper-left) origin coords */
xorig = 0;
yorig = 0;
xsize = 500;
ysize = 500;
ratio = (float) xsize / MAXRESOLUTION; /* calculate ratio to scale
full tablet into window */
/* get an appropriate visual */
vi = glXChooseVisual(dpy, DefaultScreen(dpy), attributeList);
if (vi == NULL) {
printf("Couldn't get default visual (???)\n");
exit(0);
}
/* create a GLX context */
cx = glXCreateContext(dpy, vi, NULL, GL_TRUE);
if (cx == NULL) {
printf("Couldn't get context.\n");
exit(0);
}
/* create a colormap */
cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
vi->visual, AllocAll);
/* create a window */
swa.colormap = cmap;
swa.border_pixel = 0;
/* express interest in certain events */
swa.event_mask = StructureNotifyMask | ButtonPressMask |
ButtonReleaseMask | ExposureMask;
glwin = XCreateWindow(dpy, RootWindow(dpy, vi->screen),
xorig, yorig, xsize, ysize,
0, vi->depth, InputOutput, vi->visual,
CWBorderPixel|CWColormap|CWEventMask, &swa);
XMapWindow(dpy, glwin);
XIfEvent(dpy, &event, WaitForNotify, (char*)glwin);
XSync(dpy, GL_FALSE);
/* connect the context to the window */
if (!glXMakeCurrent(dpy, glwin, cx) == GL_TRUE) {
fprintf(stderr, "error w/glXMakeCurrent: cudn't set");
fprintf(stderr, " context to the singlebuffered GL window\n");
exit(-1);
}
/* specify the values for the Window Size Hints we want to enforce: this
* window's aspect ratio needs to stay at 1:1, constrain min and max
* window size, and specify the initial origin and size of the window.
*/
Winhints.x = xorig; /* specify desired upper-left corner origin */
Winhints.y = yorig; /* of window so prog will place itself */
Winhints.width = xsize; /* specify desired x/y size of window */
Winhints.height = ysize;
Winhints.min_width = xsize/4; /* define min and max */
Winhints.max_width = scrnheight-1; /* width and height */
Winhints.min_height = ysize/4;
Winhints.max_height = scrnheight-1;
Winhints.min_aspect.x = 1; /* keep aspect at a 1:1 ratio */
Winhints.max_aspect.x = 1;
Winhints.min_aspect.y = 1;
Winhints.max_aspect.y = 1;
/* set the corresponding flags */
Winhints.flags = USPosition|USSize|PMaxSize|PMinSize|PAspect;
XSetNormalHints(dpy, glwin, &Winhints);
/* define string that will show up in the window title bar (and icon) */
XStoreName(dpy, glwin, "opengl tablet \"line drawing\" program");
/* declare interest in events we want the window to process */
XSelectInput(dpy, glwin, StructureNotifyMask | ExposureMask |
ButtonPressMask | ButtonReleaseMask);
XSetWMColormapWindows(dpy, glwin, &glwin, 1);
/* express interest in WM killing this app */
if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", GL_TRUE)) != None)
XSetWMProtocols(dpy, glwin, &del_atom, 1);
colorstruct.pixel = YELLOW;
colorstruct.red = 65535;
colorstruct.green = 65535;
colorstruct.blue = 0;
colorstruct.flags = DoRed | DoGreen | DoBlue;
XStoreColor(dpy, cmap, &colorstruct);
colorstruct.pixel = BLUE;
colorstruct.red = 0;
colorstruct.green = 0;
colorstruct.blue = 35535;
colorstruct.flags = DoRed | DoGreen | DoBlue;
XStoreColor(dpy, cmap, &colorstruct);
colorstruct.pixel = CYAN;
colorstruct.red = 0;
colorstruct.green = 65535;
colorstruct.blue = 65535;
colorstruct.flags = DoRed | DoGreen | DoBlue;
XStoreColor(dpy, cmap, &colorstruct);
glLineWidth(3.0);
glFlush();
}
/* setupdevs -
*
* establish a live connection to the tablet device.
*
* leverages off the "X11 Input Extension Library Specification"
* document (you *shud* be able to locate the on-line public access
* directory which contains all the files to print hard-copy of this
* document under .../mit/doc/extensions/xinput). refer to
* /usr/include/X11/extensions/{XI.h, XInput.h} for structures accessed.
*/
static void setupdevs() {
int i, ndevices;
XDevice *tablet_device;
XDeviceInfoPtr lp, list;
int num_ext_event_classes;
XEventClass ListOfEventClass[3];
int tablet_press_class, tablet_release_class, tablet_motion_class;
/* get a ptr to the list of all currently defined input devices */
list = (XDeviceInfoPtr) XListInputDevices(dpy, &ndevices);
if (!list) {
fprintf(stderr,"XlistInputDevices failed to generate a devices list\n"); exit(1);
}
/* check out the /usr/people/4Dgifts/examples/devices/input/X/Xlist.c
* program (which gets compiled into "xlist"). running it will list
* all the currently available input devices on the machine xlist is
* run on. there is a LOT that should be studied in the "input" subtree.
*/
for (lp=list, i=0; i<ndevices; lp++, i++) {
if (lp->use == IsXExtensionDevice && strcmp(lp->name,"tablet") == 0) {
break; /* found the right one--now save the ptr (lp) to it */
}
}
if (i == ndevices) {
fprintf(stderr, "\"tablet\" device not found\n");
exit(1);
}
tablet_device = XOpenDevice(dpy, lp->id); /* open the Tablet device */
if (!tablet_device) {
fprintf(stderr, "XOpenDevice failedfor \"tablet\" device\n");
exit(1);
}
tablet_device_id = tablet_device->device_id;
/* the following 3 macros determine the given event's type and class.
* each macro is passed the structure that describes the device from
* which input is desired.
*/
DeviceButtonPress(tablet_device, tablet_press_type, tablet_press_class);
DeviceButtonRelease(tablet_device, tablet_release_type, tablet_release_class);
DeviceMotionNotify(tablet_device, tablet_motion_type, tablet_motion_class);
ListOfEventClass[0]=tablet_press_class;
ListOfEventClass[1]=tablet_release_class;
ListOfEventClass[2]=tablet_motion_class;
num_ext_event_classes = 3;
/* XSelectExtensionEvent requests the server to send events that match
* the events and devices described by the event list and that come
* from the requested window.
*/
XSelectExtensionEvent(dpy, glwin, ListOfEventClass, num_ext_event_classes);
}
/* draw the tablet's current line segment now that the event queue is drained
*/
static void drawcurrentline(void)
{
struct lineElement *ptr;
#ifdef GIVES_BROKEN_LINE__NOT_SURE_WHY
long vect[2];
glIndexi(CYAN);
glBegin(GL_LINES); /* draw our current line segment */
for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
vect[0] = (long) (ptr->xy[0]*ratio);
vect[1] = (long) (ptr->xy[1]*ratio);
glVertex2i(vect[0], vect[1]);
}
glEnd();
#endif
int vect1[2];
int vect2[2];
glIndexi(CYAN);
for (vect1[0] = (int) (lineLs.head->xy[0]*ratio), /* initially get the */
vect1[1] = (int) (lineLs.head->xy[1]*ratio), /* first vertex */
ptr = lineLs.head->next; ptr->next != NULL; ptr = ptr->next) {
vect2[0] = (int) (ptr->xy[0]*ratio); /* get the next vertex */
vect2[1] = (int) (ptr->xy[1]*ratio);
glBegin(GL_LINES); /* draw our current line segment */
glVertex2i(vect1[0], vect1[1]);
glVertex2i(vect2[0], vect2[1]);
glEnd();
vect1[0] = vect2[0]; /* now save the last vertex into the */
vect1[1] = vect2[1]; /* beginning of the next line segment */
}
}
/* makeframe -- Draw the tablet "background" in the GL window
*/
static void makeframe()
{
glClearIndex((GLfloat) BLUE);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
gluOrtho2D(-0.5, xsize-0.5, -0.5, ysize-0.5);
glIndexi(YELLOW);
glRasterPos2i(4, 5);
printString("Use left mouse button to quit.");
glFlush();
}
/* makeRasterFont() and printString() are lifted out of font.c (lives in
* this same directory) as a replacement to the IrisGL charstr() function.
*/
GLuint base;
static void makeRasterFont(Display **dpy)
{
XFontStruct *fontInfo;
Font id;
unsigned int first, last;
fontInfo = XLoadQueryFont(*dpy,
"-sgi-screen-bold-r-normal--15-150-72-72-m-90-iso8859-1");
if (fontInfo == NULL) {
printf("no font found\n");
exit(0);
}
id = fontInfo->fid;
first = fontInfo->min_char_or_byte2;
last = fontInfo->max_char_or_byte2;
base = glGenLists(last+1);
if (base == 0) {
printf("out of display lists\n");
exit(0);
}
glXUseXFont(id, first, last-first+1, base+first);
}
static void printString(char *s)
{
glPushAttrib(GL_LIST_BIT);
glListBase(base);
glCallLists(strlen(s), GL_UNSIGNED_BYTE, (unsigned char *)s);
glPopAttrib();
}
/* clean_exit -- Clean up before exiting
*/
static void clean_exit(void)
{
XCloseDisplay(dpy);
exit(0);
}